package javase.ch02.sync.synchronizedandlock;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @Desc TODO
 * @Author chenmin
 * @Date 2025/10/30
 *
 * 可重入锁：在执行对象中所有同步方法不用再次获得锁
 *      1.指在同一个线程在外层方法获取锁的时候，再进入该线程的内层方法会自动获取锁(前提，锁对象得是同一个对象)，不会因为之前已经获取过还没释放而阻塞。
 *      2.在一个synchronized修饰的方法或代码块的内部调用本类的其他synchronized修饰的方法或代码块时，是永远可以得到锁的
 *      3.Java中ReentrantLock和synchronized都是可重入锁，可重入锁的一个优点是可一定程度避免死锁。
 *
 * 如果是1个有 synchronized 修饰的递归调用方法，程序第2次进入被自己阻塞了岂不是天大的笑话，出现了作茧自缚。
 */
public class ReEnterLockDemo {
    //对象锁
    private byte[] bytes = new byte[0];

    //一把可重入锁
    private final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        ReEnterLockDemo re = new ReEnterLockDemo();

        //new Thread(()->re.m1() , "线程").start();

        //new Thread(()->re.m2() , "线程").start();

        Thread t1 = new Thread(() -> re.method1(), "线程1");
        Thread t2 = new Thread(() -> re.method1(), "线程2");
        t1.start();
        t2.start();
    }

    //同步代码块的嵌套
    public void m1(){
        synchronized (bytes){
            System.out.println("----------外层----------");
            synchronized (bytes){
                System.out.println("----------中层----------");
                synchronized (bytes){
                    System.out.println("----------内层----------");
                }
            }
        }
    }

    //同步方法的嵌套  对象锁this
    public synchronized void m2(){
        System.out.println("-------m2()-------");
        m3();
    }
    public synchronized void m3(){
        System.out.println("-------m3()-------");
        m4();
    }
    public synchronized void m4(){
        System.out.println("-------m4()-------");
    }

    //Lock锁  ReentrantLock可重入锁
    public void method1(){
        lock.lock();
        try{
            System.out.println("-------method1-1层--------");
            lock.lock();
            try{
                System.out.println("-------method1-2层--------");
            }catch(Exception e){
                e.printStackTrace();
            }finally {
                //提问：加锁次数和释放锁的次数不一致，会怎么样？
                //回答：在finally中调用unlock()进行释放锁，确保加锁次数和释放次数一样，不然很容易造成线程死锁
                //lock.unlock();
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

}
